home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / drgnsmth.cpt / Dragonsmith 1.1 / Base files / Classes / Preferences.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-10  |  11.7 KB  |  371 lines

  1. /*
  2.     Preferences.c
  3.     
  4.     A resource-based preferences class ╤ automatically handles all resource manipulations.
  5.     Very few applications should need to subclass this class.
  6.  
  7.     Copyright ⌐ 1992 by Paul M. Hoffman
  8.     Send comments or suggestions to paul.hoffman@um.cc.umich.edu
  9.     
  10.     This source code may be freely used, altered, and distributed in any way as long as:
  11.         1.    It is GIVEN away rather than sold (except as expressly permitted by the author)
  12.         2.    This statement and the above copyright notice are left intact.
  13.         
  14.     This class was created as part of the Dragonsmith drag-and-drop application development kit,
  15.     but may be used as is by any application that uses THINK C's OOP extensions ╤ although you
  16.     will need to add more #include's to get it to compile correctly (or you can use the same precompiled
  17.     headers that Dragonsmith does)
  18.     
  19.     See the file "Preferences class notes" for a brief description of this class
  20.     
  21. */
  22.  
  23. #include    "Preferences.h"
  24.  
  25. Preferences::Preferences (void)
  26. {
  27.     appResFork = kInvalidRefNum;
  28.     fileCreator = '????';                            // These two will be set
  29.     fileType = '????';                            //    in the Init method
  30.     prefsRoster = NULL;
  31.     numPrefs = 0;
  32.     file.name[0] = 0;
  33.     fileResFork = kInvalidRefNum;
  34.     fileExists = FALSE;
  35.     canSaveFile = FALSE;
  36.     usingAppResources = TRUE;                    // This is the safer default
  37. }
  38.  
  39. Boolean Preferences::Init (short appRF, OSType creator, OSType type)
  40. {
  41.     // Initialize preferences using the resources in the application's file.  This method's caller should treat a return
  42.     //    value of FALSE as a fatal error.  The only thing that might cause this is if the application's file is messed up
  43.     //    (i.e., it's lacking some vital resources or the 'PrRo' resource has been corrupted)
  44.     
  45.     fileCreator = creator;
  46.     fileType = type;
  47.     appResFork = appRF;                        // We can use the application's resources if we have to
  48.     if (appResFork == kInvalidRefNum)
  49.         return FALSE;
  50.     prefsRoster = InitPrefsRoster (appResFork);
  51.     if (prefsRoster != NULL && VerifyResources (prefsRoster)) {
  52.         // Now initialize instance variables for the newly opened application file and return successfully
  53.         numPrefs = (*prefsRoster)->numRsrcs;
  54.         fileExists = FALSE;
  55.         usingAppResources = TRUE;
  56.         canSaveFile = FALSE;
  57.         return TRUE;
  58.     } else
  59.         return FALSE;
  60. }
  61.  
  62. Preferences::~Preferences (void)
  63. {
  64.     // Requires:    fileExists, fileResFork, canSaveFile
  65.     // Changes:    <not applicable>
  66.  
  67.     CloseFile ();        // This will automatically save any changes if appropriate
  68. }
  69.  
  70. Boolean Preferences::UseFile (FSSpec *fss)
  71. {
  72.     // Switch to another preferences file (if fss != NULL)
  73.     
  74.     // Requires:    prefsRoster, numPrefs, fileExists, fileResFork, canSaveFile
  75.     // Changes:    <if it succeeds, all but appResFork, fileCreator, and fileType ╤ otherwise, none>
  76.  
  77.     char                perm = fsRdWrPerm;
  78.     short            refNum = kInvalidRefNum;
  79.     PrefsRosterList    **roster;
  80.     OSErr            err;
  81.     
  82.     if (fss == NULL)
  83.         return FALSE;
  84.     
  85.     // Open the file with either fsRdWrPerm or fsRdPerm ╤ on return, perm will contain the permission that was granted
  86.     err = FSpOpenResFork (fss, &refNum, &perm);
  87.     if (err == noErr && refNum != kInvalidRefNum) {
  88.         roster = InitPrefsRoster (refNum);
  89.         if (roster == NULL && perm == fsRdWrPerm && err == noErr) {
  90.         
  91.             // If we couldn't initialize the preferences roster from the file we want to use, then we copy everything from the
  92.             //    current preference file's resource fork to the newly opened file (so we have to have write access!)
  93.             err = CopyPrefs (refNum);            // Ensure that any prefs resources not already in the file will be copied
  94.                                             //    from the current prefs file (or the application)
  95.             roster = InitPrefsRoster (refNum);        // Try to initialize the preferences roster for the new file
  96.         }
  97.         if (roster != NULL && err == noErr && VerifyResources (roster)) {
  98.         
  99.             // Save and close the file we were using (CloseFile does both, plus it disposes of the old prefs resources roster)
  100.             CloseFile ();
  101.             
  102.             // Now initialize instance variables for the newly opened file and return successfully
  103.             prefsRoster = roster;
  104.             numPrefs = (*prefsRoster)->numRsrcs;
  105.             file = *fss;
  106.             fileResFork = refNum;
  107.             fileExists = TRUE;
  108.             usingAppResources = FALSE;
  109.             canSaveFile = (perm == fsRdWrPerm);
  110.             return TRUE;
  111.         } else if (refNum != appResFork)
  112.             CloseResFile (refNum);
  113.     }
  114.     return FALSE;
  115. }
  116.  
  117. Boolean Preferences::VerifyResources (PrefsRosterList **roster)
  118. {
  119.     return TRUE;
  120.  
  121.     // Override this method to provide verification of the specified roster ╤ your method shouldn't rely on any of this class's
  122.     //    instance variables being valid, nor should it change any of them (though the same does not necessarily apply to
  123.     //    the instance variables which your subclass adds ╤ that's up to you)
  124.     
  125.     // This method is called by Init (to test the resources in the application file) and UseFile (when we're trying to switch to
  126.     //    another file).   At the time this method is called, roster is a valid prefs resources roster containing handles to all the
  127.     //    preferences resources that could be found.  Also, (*roster)->refNum is a valid refNum to the file in which the resources
  128.     //    reside (we may or may not have write access to this file)
  129.     
  130.     // If there are any preference resources that absolutely MUST exist, then you can either try to add them by hand
  131.     //    (which will only work if you have write access to the file), or you can just do like this ╤
  132.     
  133.     /*
  134.         return (    (*roster)->rsrc[prefVital1].resHndl != NULL
  135.             &&    (*roster)->rsrc[prefVital2].resHndl != NULL
  136.             &&    (*roster)->rsrc[prefVital3].resHndl != NULL
  137.             &&    (*roster)->rsrc[prefVital4].resHndl != NULL);
  138.     */
  139.     
  140.     // Of course, you might also need to check for empty handles╔
  141.     
  142.     // If VerifyResources returns FALSE, then the file being verified (i.e., (*roster)->refNum) will not be used.  If it's the app file,
  143.     //    then this is a fatal error and the app should quit.  Otherwise, remember ╤ THEY'RE JUST PREFERENCES!!  Your app
  144.     //    can still function without them
  145. }
  146.  
  147. OSErr Preferences::CopyPrefs (short destRF)
  148. {
  149.     // Requires:    prefsRoster, numPrefs
  150.     // Changes:    <none>
  151.  
  152.     short            saveRF, i;
  153.     Handle            h;
  154.     OSErr            err = noErr;
  155.     PrefsRosterList    **tempRoster = NULL;
  156.     
  157.     if (destRF == kInvalidRefNum)
  158.         return rfNumErr;
  159.     
  160.     saveRF = CurResFile ();
  161.     if (saveRF != destRF)
  162.         UseResFile (destRF);
  163.     
  164.     h = Get1Resource (rPrefsRosterType, rPrefsRosterID);    // Look to see if this file already has a 'PrRo' resource
  165.     if (h != NULL) {                                        //    ╤ if it does, get rid of it
  166.         RmveResource (h);
  167.         DisposHandle (h);
  168.     }
  169.  
  170.     // Make a copy of the current prefsRoster ╤ it would turn into a resource handle if we AddResource'd it (ugh!)
  171.     tempRoster = prefsRoster;
  172.     err = HandToHand ((Handle *) &tempRoster);
  173.     if (err == noErr) {
  174.         (*tempRoster)->refNum = kInvalidRefNum;        // Wipe out any run-time specific
  175.         for (i = 0; i < numPrefs; i++)                    //    values in the refNum field
  176.             (*tempRoster)->rsrc[i].resHndl = NULL;        //    and resHndl fields of the copy
  177.         AddResource ((Handle) tempRoster, rPrefsRosterType, rPrefsRosterID, NULL);
  178.         err = ResError ();
  179.     }
  180.     
  181.     // This loop won't be executed if AddResource failed        
  182.     for (i = 0; i < numPrefs && err == noErr; i++)
  183.         err = CopyResHandle (GetPrefResource (i), destRF, FALSE);
  184.     
  185.     if (err == noErr)
  186.         UpdateResFile (destRF);
  187.  
  188.     if (saveRF != destRF)
  189.         UseResFile (saveRF);
  190.     
  191.     return err;
  192. }
  193.  
  194. Handle Preferences::GetPrefResource (short index)
  195. {
  196.     // Requires:    prefsRoster, numPrefs
  197.     // Changes:    <none>
  198.     
  199.     Handle    h;
  200.     
  201.     if (index < 0 || index >= numPrefs)
  202.         return NULL;
  203.     
  204.     h = (*prefsRoster)->rsrc[index].resHndl;
  205.     if (h == NULL)
  206.         return ReadPrefResource (index);
  207.     else if (*h == NULL)
  208.         LoadResource (h);
  209.     
  210.     return h;
  211. }
  212.  
  213. Handle Preferences::ReadPrefResource (short index)
  214. {
  215.     // Requires:    prefsRoster
  216.     // Changes:    <none>
  217.  
  218.     short    useRefNum, saveRefNum;
  219.     Handle    h;
  220.     
  221.     useRefNum = (*prefsRoster)->refNum;        // Use the file in which the resources in the current prefsRoster reside
  222.     if (useRefNum != kInvalidRefNum) {
  223.         saveRefNum = CurResFile ();
  224.         if (saveRefNum != useRefNum)
  225.             UseResFile (useRefNum);
  226.         h = (*prefsRoster)->rsrc[index].resHndl = Get1Resource ((*prefsRoster)->rsrc[index].resType, (*prefsRoster)->rsrc[index].resID);
  227.         if (saveRefNum != useRefNum)
  228.             UseResFile (saveRefNum);
  229.         return h;
  230.     } else
  231.         return NULL;
  232. }
  233.  
  234. OSErr Preferences::DetachPrefResource (short index)
  235. {
  236.     OSErr    err = resNotFound;
  237.     Handle    h;
  238.     
  239.     if (index >= 0 && index <= numPrefs) {
  240.         h = (*prefsRoster)->rsrc[index].resHndl;
  241.         if (h != NULL) {
  242.             DetachResource (h);
  243.             if ((err = ResError ()) == noErr)
  244.                 (*prefsRoster)->rsrc[index].resHndl = NULL;
  245.         }
  246.     }
  247.     return err;
  248. }
  249.  
  250. OSErr Preferences::ReleasePrefResource (short index)
  251. {
  252.     OSErr    err = resNotFound;
  253.     Handle    h;
  254.     
  255.     if (index >= 0 && index <= numPrefs) {
  256.         h = (*prefsRoster)->rsrc[index].resHndl;
  257.         if (h != NULL) {
  258.             ReleaseResource (h);
  259.             if ((err = ResError ()) == noErr)
  260.                 (*prefsRoster)->rsrc[index].resHndl = NULL;
  261.         }
  262.     }
  263.     return err;
  264. }
  265.  
  266. void Preferences::CloseFile (void)
  267. {
  268.     // Close the current preferences file (this will save it if possible) and dispose of the preferences resources roster
  269.     
  270.     // Requires:    fileResFork
  271.     // Changes:    fileResFork, fileExists, canSaveFile, usingAppResources
  272.     // Disposes:    prefsRoster
  273.  
  274.     Handle    h;
  275.     short    i;
  276.     
  277.     // If we're using a preferences file, close it (saving any resources which have been ChangedResource'd) ╤ this
  278.     //    automatically releases all the resources referred to in prefsRoster
  279.     if (fileResFork != kInvalidRefNum) {
  280.         CloseResFile (fileResFork);
  281.         fileResFork = kInvalidRefNum;
  282.          if (fileExists)
  283.             FlushVol (NULL, file.vRefNum);
  284.         fileExists = FALSE;
  285.         canSaveFile = FALSE;
  286.         usingAppResources = TRUE;
  287.     }
  288.     
  289.     // Now release the preferences resources roster itself
  290.     if (prefsRoster != NULL) {
  291.     
  292.         // If the resources came from the application file, special care must be taken to release everything by hand,
  293.         //    since the code above wasn't executed, so the application file wasn't closed (which it shouldn't be, of course)
  294.         if ((*prefsRoster)->refNum == appResFork) {        // Could also say "if (usingAppResources)", but I prefer this
  295.             for (i = 0; i < numPrefs; i++) {
  296.                 h = (*prefsRoster)->rsrc[i].resHndl;
  297.                 if (h != NULL)
  298.                     ReleaseResource (h);
  299.             }
  300.         }
  301.         DisposHandle ((Handle) prefsRoster);
  302.     }
  303. }
  304.  
  305. OSErr Preferences::SavePrefResource (short index)
  306. {
  307.     // Mark the designated resource as changed, and (if possible) 
  308.     // Requires:    prefsRoster, numPrefs
  309.     // Changes:    <none>
  310.  
  311.     Handle    h;
  312.     char        hState;
  313.     OSErr    err;
  314.     
  315.     if (canSaveFile && index >= 0 && index < numPrefs) {
  316.         h = (*prefsRoster)->rsrc[index].resHndl;
  317.         if (h == NULL)
  318.             return resNotFound;    // The resource doesn't exist
  319.         if (*h == NULL)
  320.             return noErr;            // It's already saved (and it's purged)
  321.         hState = HGetState (h);
  322.         HNoPurge (h);
  323.         ChangedResource (h);
  324.         err = ResError ();            // Inside Macintosh, Volume I (page 125) warns about not checking ResError here
  325.         if (err == noErr)
  326.             WriteResource (h);
  327.         HSetState (h, hState);
  328.         return ResError ();
  329.     } else
  330.         return fLckdErr;            // Return an error as if the file were locked (which in fact it may be╔)
  331. }
  332.  
  333. PrefsRosterList **Preferences::InitPrefsRoster (short refNum)
  334. {
  335.     // Requires:    <none>
  336.     // Changes:    <none>
  337.  
  338.     short            saveRF, n;
  339.     register short        i;
  340.     PrefsRosterList    **roster, **prefsRosterWas;
  341.     
  342.     saveRF = CurResFile ();
  343.     if (saveRF != refNum)
  344.         UseResFile (refNum);
  345.         
  346.     roster = (PrefsRosterList **) Get1Resource (rPrefsRosterType, rPrefsRosterID);
  347.     if (roster != NULL) {
  348.         DetachResource ((Handle) roster);        // The prefs roster should never be changed except in memory
  349.         HNoPurge ((Handle) roster);            // Just in case the 'PrRo' resource's attributes weren't set correctly
  350.         
  351.         (*roster)->refNum = refNum;                // Store a copy of the file's refNum for safekeeping
  352.         n = (*roster)->numRsrcs;
  353.         if (n < 1) {
  354.             DisposHandle ((Handle) roster);
  355.             roster = NULL;
  356.         } else {
  357.             prefsRosterWas = prefsRoster;        // Save the original value of prefsRoster
  358.             prefsRoster = roster;
  359.             for (i = 0; i < n; i++)
  360.                 (void) ReadPrefResource (i);        // Ignore any errors
  361.             prefsRoster = prefsRosterWas;        // Restore prefsRoster
  362.         }
  363.     }
  364.     
  365.     if (saveRF != refNum)
  366.         UseResFile (saveRF);
  367.     
  368.     return roster;
  369. }
  370.  
  371.